package com.bkhech.nettyhttp.client;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

/**
 * SslContextFactoryMutualAuthForClient
 * 开启双向验证时，客户端生产环境中需安全的证书管理策略示例
 * <p>
 * 双向验证：客户端和服务器都开启了https验证，需要相互验证对方的请求
 * <p>
 * 实际生产环境一般只服务器那边开启单向验证，
 * 因为开启 https 要比 http 慢，在安全和速度上进行权衡。
 *
 * @author guowm
 * @date 2024/4/1
 */
public class SslContextFactoryMutualAuthForClient {
    //Java特有的密钥存储格式
    private static final String KEY_STORE_TYPE = "JKS";

    // Bouncy Castle 的 X.509 证书管理器。
    private static final String KEY_MANAGER_TYPE = "X509";

    //JKS 格式的自己证书的实际路径(若自己要开启https验证，则需要将自己的证书加载到 KeyManagerFactory 中)。
    private static final String KEY_STORE_PATH = "/path/to/your/keystore.jks";
    //JKS 格式的自己证书的密码。
    private static final String KEY_STORE_PASSWORD = "your_keystore_password";

    private static final String TRUST_MANAGER_TYPE = "X509";
    //JKS 格式的服务器证书的实际路径(信任的对方的证书，此处对方为服务器，将信息加载到 TrustManagerFactory 中)。
    private static final String TRUST_STORE_PATH = "/path/to/your/truststore.jks";
    //JKS 格式的服务器证书的密码(信任的对方的证书密码)。
    private static final String TRUST_STORE_PASSWORD = "your_truststore_password";

    public static SslContext getSslContext() throws Exception {
        KeyManagerFactory keyManagerFactory = buildKeyManagerFactory();
        TrustManagerFactory trustManagerFactory = buildTrustManagerFactory();

        return SslContextBuilder.forClient()
                .keyManager(keyManagerFactory)
                .trustManager(trustManagerFactory)
                .build();
    }

    /**
     * 自己证书管理工厂
     *
     * @return
     * @throws Exception
     */
    private static KeyManagerFactory buildKeyManagerFactory() throws Exception {
        final KeyStore keyStore = loadKeyStore(KEY_STORE_PATH, KEY_STORE_PASSWORD);

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KEY_MANAGER_TYPE);
        keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());
        return keyManagerFactory;
    }

    /**
     * 信任证书管理工厂
     *
     * @return
     * @throws Exception
     */
    private static TrustManagerFactory buildTrustManagerFactory() throws Exception {
        final KeyStore trustStore = loadKeyStore(TRUST_STORE_PATH, TRUST_STORE_PASSWORD);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TRUST_MANAGER_TYPE);
        trustManagerFactory.init(trustStore);
        return trustManagerFactory;
    }

    private static KeyStore loadKeyStore(String storePath, String storePassword) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
        try (FileInputStream fis = new FileInputStream(storePath)) {
            keyStore.load(fis, storePassword.toCharArray());
        }
        return keyStore;
    }
}
